#include "AlignResult.h"
#include "compress_algo.h"

AlignResultRead::AlignResultRead(string fileName) {
	_numInBuf = 0;
	_resultBuf = new AlignResult();
	_numWinRead = 0;

	/*read the meta data*/
	_fileName = fileName;
	safe_fopen(&_alignFile, _fileName.c_str(), "rb");
	safe_fopen(&_metaFile, string(_fileName + SUFFIX_META).c_str(), "rb");

	size_t strSize = 0;
	safe_fread(&strSize, sizeof(size_t), 1, _metaFile);
	char* chrName = new char[strSize + 10];
	safe_fread(chrName, sizeof(char), strSize, _metaFile);
	_chrName = string(chrName, strSize);
	safe_fread(&_readLength, sizeof(unsigned int), 1, _metaFile);
	safe_fread(&_refLength, sizeof(unsigned int), 1, _metaFile);
	safe_fread(&_winSize, sizeof(unsigned int), 1, _metaFile);
	safe_fread(&_bucketSize, sizeof(unsigned int), 1, _metaFile);
	safe_fread(&_numBucket, sizeof(unsigned int), 1, _metaFile);
	_writePos = new vector<BucketPos>[_numBucket];

	cout << "read meta data done." << endl;

	cout << "_chrName = " << _chrName << endl;
	cout << "_readLength = " << _readLength << endl;
	cout << "_refLength = " << _refLength << endl;
	cout << "_winSize = " << _winSize << endl;
	cout << "_bucketSize = " << _bucketSize << endl;
	cout << "_numBucket = " << _numBucket << endl;

	_resultBuf->_chrName = _chrName;

	/*read the offset data for each bucket*/
	unsigned int bucketSize = 0;
	for(unsigned int i = 0; i < _numBucket; i++) {
		unsigned int bucketSize = 0;
		safe_fread(&bucketSize, sizeof(unsigned int), 1, _metaFile);
		for(int j = 0; j < bucketSize; j++) {
			BucketPos pos;
			safe_fread(&pos, sizeof(BucketPos), 1, _metaFile);
			_writePos[i].push_back(pos);	
		}
	}
	//printWritePos();
	
	cout << "read bucket data done." << endl;
}

AlignResultRead::~AlignResultRead() {
	delete[] _writePos;
	delete _resultBuf;
	safe_fclose(_alignFile);
	safe_fclose(_metaFile);
}

bool AlignResultRead::fetchOrdered(char* read, char* qual, int* hit, int* readLen, 
					unsigned int* pos, char* strand, 
					uchar2* offset, char2* sub) {
	if(_numInBuf == 0) {
		_resultBuf->close();

		if(_numWinRead + 1 <= _numBucket) {
			while(fetchWindow(_resultBuf, _numWinRead) <= 0) {
				_numWinRead += 1;
				if(_numWinRead == _numBucket) {
					return false;
				}
			}
			//this step currently is necessary
			//_resultBuf->sort();
			_resultBuf->cpu_sort();
			_numWinRead += 1;
			_numInBuf = _resultBuf->_numAlign;
		} else {
			return false;
		}	
	}
	assert(_numInBuf > 0);

	const unsigned int qid = _resultBuf->_numAlign - _numInBuf;
	char* _qual = _resultBuf->get_qscore(qid);
	for(int i = 0; i < _readLength; i++) {
		qual[i] = _qual[i];
	}
	_resultBuf->get_base(read, qid);	
	*hit = _resultBuf->_hit[qid];
	*readLen = _readLength;
	*pos = _resultBuf->_pos[qid];
	*strand = _resultBuf->_strand[qid];


	if(offset != NULL && sub != NULL) {
		*offset = _resultBuf->_offset[qid];
		*sub = _resultBuf->_sub[qid];
	}

	_numInBuf--;

	return true;
}


//TODO: allocate the buffer using a dynamical way (reduce the allocation overhead)
//
unsigned int AlignResultRead::fetchWindow(AlignResult* result, const unsigned int winId) {
	assert(winId < _numBucket);

	result->_numAlign = 0;
	result->_readLength = _readLength;
	vector<BucketPos>::iterator it;
	unsigned long offset = 0;

	unsigned int numAlign = 0;
	for(it = _writePos[winId].begin(); it < _writePos[winId].end(); it++) {
		numAlign += it->_numAlign;
	}
	if(numAlign == 0) {
		return 0;
	}

	result->_qscore = new char[_readLength*numAlign];
	result->_pos = new unsigned int[numAlign];
	result->_hit = new int[numAlign];
	result->_strand = new char[numAlign];
	result->_offset = new uchar2[numAlign];
	result->_sub = new char2[numAlign];


	for(it = _writePos[winId].begin(); it < _writePos[winId].end(); it++) {
		offset = it->_offset;
		safe_fseek(_alignFile, offset, SEEK_SET);
		
		safe_fread(result->_qscore + _readLength*(result->_numAlign), sizeof(unsigned char), 
				_readLength*it->_numAlign, _alignFile);
		safe_fread(result->_pos + result->_numAlign, sizeof(unsigned int), it->_numAlign, _alignFile);
		safe_fread(result->_hit + result->_numAlign, sizeof(int), it->_numAlign, _alignFile);
		
		//w/o compression
		//safe_fread(result->_strand + result->_numAlign, sizeof(char), it->_numAlign, _alignFile);
		//w/ compression
		const unsigned int numWord = ceil(it->_numAlign/(double)(sizeof(unsigned int)*8.0));
		unsigned int* word = new unsigned int[numWord];
		safe_fread(word, sizeof(unsigned int), numWord, _alignFile);
		bitDecode(result->_strand + result->_numAlign, it->_numAlign, word, numWord);
		delete[] word;

		safe_fread(result->_offset + result->_numAlign, sizeof(uchar2), it->_numAlign, _alignFile);
		safe_fread(result->_sub + result->_numAlign, sizeof(char2), it->_numAlign, _alignFile);

		result->_numAlign += it->_numAlign;
	}
	assert(result->_numAlign == numAlign);

	return result->_numAlign;
}

